Group-stepwise ORDC, load reserve provision, and OfflineReserve ORDC supply - #236
Group-stepwise ORDC, load reserve provision, and OfflineReserve ORDC supply#236rodrigomha wants to merge 18 commits into
Conversation
|
New commit |
|
@rodrigomha this PR needs to be rebased |
3479231 to
f33d289
Compare
|
Done with the rebase |
|
Performance Results
|
|
CI status after the rebase, fully diagnosed:
|
…supply
One demand curve on a PSY.GroupReserve is cleared by the summed awards of its
contributing services: a dense ServiceRequirementVariable per group, a clearing
constraint sum(member awards) >= demand variable (its dual is the group price),
and the group curve priced through the delta-PWL path. Static and time-series
group curves are both supported via the existing service-side TS machinery.
- Group demand predicates mirror the service formulations: GroupRangeReserve is
driven by the scalar requirement, GroupStepwiseCostReserve by the demand curve;
degenerate groups skip as supply aggregates with a warning.
- Group deferral generalized to a vector so up and down groups coexist.
- RESERVE_PRODUCT_TYPES (definitions.jl) consolidates the open
Union{PSY.AbstractReserve, PSY.GroupReserve} signature bound used across the
reserve traits, PWL parameter chain, and objective plumbing.
- Formulation-pairing guards: a GroupReserve accepts only group formulations and
vice versa, failing with ArgumentError at ServiceModel declaration.
- Tests cover build/solve, aggregation binding, merit order, no-group baseline,
the degenerate skip, TS group curves, and the pairing guards.
PSY moved GroupReserve into the reserve tree, so the RESERVE_PRODUCT_TYPES
alias and the per-type methods that existed only because groups sat outside
it are gone: every former Union bound is plain PSY.AbstractReserve, the
group get_initial_conditions_service_model and the CostExpressions container
sibling fold into the AbstractReserve methods, and uses_compact_power opens
to the abstract type. Formulation-pair bounds
(Union{StepwiseCostReserve, GroupStepwiseCostReserve}) and the group demand
predicates stay - they encode formulation semantics, not typing.
The pairing guards merge with the #235 hardening set: valid direction-applied
pairs for both group formulations, the generic-defaults disambiguator, the
inverse guard over both formulations, and a direction-required error for
bare GroupReserve declarations.
A controllable load routes reserves inversely to generators: up reserve is committed shed (P - r_up >= 0), down reserve is committed extra consumption (P + r_down <= forecast). Dispatch limits move to the range expressions only when a reserve service is attached, and a costless load selling reserves fails loudly since nothing pins its consumption.
…-out End-to-end market test: an elastic OnlineReserve (StepwiseCostReserve) and a GroupStepwiseCostReserve group co-clear against per-resource offers from thermal, storage, and load participants. Registers GroupStepwiseCostReserve in the formulation library, refreshes the stale group-reserve warnings there, and renames the remaining market-specific reserve identifiers in hydro to generic ones.
Non-spinning is upward-only, so OfflineReserve routes like an up reserve everywhere a device supplies it. New UP_RESERVE union in reserve_traits.jl; storage reserve-balance multipliers, coverage branches (two of which silently skipped OfflineReserve, one asserted), get_fraction, and the TotalReserveOffering fold widened; load routing and folding accept it as committed shed. The _modify_device_model! no-op is scoped to NonSpinningReserve, whose awards ride ReservePowerConstraint instead of the device range expressions.
PowerSystems psy6 (post schema-matching merge) depends on the unregistered PowerCoreOpenAPIModels / PowerOperationsOpenAPIModels. Pkg ignores [sources] of non-root projects, so each environment that resolves PSY - root, test, docs - must pin them itself; CI failed with 'PowerOperationsOpenAPIModels has no known versions' on all jobs. Pins mirror PSY's own (monorepo main, subdirs) and are temporary until the packages are registered.
f0fda18 to
3382f8f
Compare
|
Restacked on #233 per the network-consolidation sequencing: base is now
|
…supply
One demand curve on a PSY.GroupReserve is cleared by the summed awards of its
contributing services: a dense ServiceRequirementVariable per group, a clearing
constraint sum(member awards) >= demand variable (its dual is the group price),
and the group curve priced through the delta-PWL path. Static and time-series
group curves are both supported via the existing service-side TS machinery.
- Group demand predicates mirror the service formulations: GroupRangeReserve is
driven by the scalar requirement, GroupStepwiseCostReserve by the demand curve;
degenerate groups skip as supply aggregates with a warning.
- Group deferral generalized to a vector so up and down groups coexist.
- RESERVE_PRODUCT_TYPES (definitions.jl) consolidates the open
Union{PSY.AbstractReserve, PSY.GroupReserve} signature bound used across the
reserve traits, PWL parameter chain, and objective plumbing.
- Formulation-pairing guards: a GroupReserve accepts only group formulations and
vice versa, failing with ArgumentError at ServiceModel declaration.
- Tests cover build/solve, aggregation binding, merit order, no-group baseline,
the degenerate skip, TS group curves, and the pairing guards.
PSY moved GroupReserve into the reserve tree, so the RESERVE_PRODUCT_TYPES
alias and the per-type methods that existed only because groups sat outside
it are gone: every former Union bound is plain PSY.AbstractReserve, the
group get_initial_conditions_service_model and the CostExpressions container
sibling fold into the AbstractReserve methods, and uses_compact_power opens
to the abstract type. Formulation-pair bounds
(Union{StepwiseCostReserve, GroupStepwiseCostReserve}) and the group demand
predicates stay - they encode formulation semantics, not typing.
The pairing guards merge with the #235 hardening set: valid direction-applied
pairs for both group formulations, the generic-defaults disambiguator, the
inverse guard over both formulations, and a direction-required error for
bare GroupReserve declarations.
A controllable load routes reserves inversely to generators: up reserve is committed shed (P - r_up >= 0), down reserve is committed extra consumption (P + r_down <= forecast). Dispatch limits move to the range expressions only when a reserve service is attached, and a costless load selling reserves fails loudly since nothing pins its consumption.
…-out End-to-end market test: an elastic OnlineReserve (StepwiseCostReserve) and a GroupStepwiseCostReserve group co-clear against per-resource offers from thermal, storage, and load participants. Registers GroupStepwiseCostReserve in the formulation library, refreshes the stale group-reserve warnings there, and renames the remaining market-specific reserve identifiers in hydro to generic ones.
Non-spinning is upward-only, so OfflineReserve routes like an up reserve everywhere a device supplies it. New UP_RESERVE union in reserve_traits.jl; storage reserve-balance multipliers, coverage branches (two of which silently skipped OfflineReserve, one asserted), get_fraction, and the TotalReserveOffering fold widened; load routing and folding accept it as committed shed. The _modify_device_model! no-op is scoped to NonSpinningReserve, whose awards ride ReservePowerConstraint instead of the device range expressions.
PowerSystems psy6 (post schema-matching merge) depends on the unregistered PowerCoreOpenAPIModels / PowerOperationsOpenAPIModels. Pkg ignores [sources] of non-root projects, so each environment that resolves PSY - root, test, docs - must pin them itself; CI failed with 'PowerOperationsOpenAPIModels has no known versions' on all jobs. Pins mirror PSY's own (monorepo main, subdirs) and are temporary until the packages are registered.
…na/PowerOperationsModels.jl into rh/new_load_and_group_services
luke-kiernan
left a comment
There was a problem hiding this comment.
Looks fine, no issues at a glance. However, you should probably have others review it too--I'm not very familiar the distinctions between these differennt types of reserves...
| end | ||
|
|
||
| # Load up-reserve is committed shed: LB = P - Σ r_up, constrained >= 0. Generators route | ||
| # ReserveUp to the UB expression, so `V <: PSY.ElectricLoad` cannot shadow them. |
There was a problem hiding this comment.
I don't follow the 2nd sentence of this comment
| """ | ||
| Direction of a reserve. `OfflineReserve` (non-spinning) has no direction type parameter and is | ||
| upward-only in every US market, so it maps to [`PSY.ReserveUp`](@ref). | ||
| upward-only in every US market, so it maps to `PSY.ReserveUp`. |
There was a problem hiding this comment.
Lost (@ref). purposeful or AI?
Stacked on #235 (base branch
rh/reserve_refactor); the diff shows only this feature's commits.New features (not refactor items) re-expressed on the psy6 reserve tree:
GroupStepwiseCostReserve: elastic group ORDC. One denseServiceRequirementVariableper group, a clearing constraintsum(member awards) >= requirement variable, and the group's demand curve (static or time series) priced through the existing delta-PWL path. Mis-pairedServiceModels (GroupReservewith a non-group formulation and vice versa) fail at declaration.PowerLoadDispatch: the inverse of a generator. Up reserve is committed shed (P - r_up >= 0), down reserve is committed extra consumption (P + r_down <= forecast), gated on an attached service model. A costless load selling reserves errors since nothing pins its consumption.OfflineReserve(non-spin) as ORDC supply from storage and loads: non-spinning is upward-only, so it routes like an up reserve everywhere a device supplies it (newUP_RESERVEunion). Previously only thermal was wired: the storage reserve-balance multipliers MethodError'd, two storage coverage branches silently skipped the service, complete coverage hit an@assert false, and_modify_device_model!no-opped everyOfflineReservemodel. The no-op is now scoped toNonSpinningReserve, whose awards rideReservePowerConstraintinstead of the device range expressions.Tests live in existing files: group-stepwise testsets in
test_services_constructor.jl; market integration, load provision, and theOfflineReserveORDC scenarios intest_device_reserve_offers.jl; the storage non-spin coverage testset intest_storage_device_models.jl.Full suite green: 106708/106708. Docs build green.
🤖 Generated with Claude Code